﻿using DataProcess.Interface;
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Text;
using System.Threading;

namespace DataDrive.FileWatch
{
    public class DataDrive : IDataDrive
    {
        private Config Config { get; set; }
        private FileSystemWatcher Watcher { get; set; }

        public event StartedHandler Started;
        public event StopedHandler Stoped;
        public event DataRecivedHandler DataRecived;
        public event ErrorNoticedHandler ErrorNoticed;
        public event WarnNoticedHandler WarnNoticed;
        public event InfoNoticedHandler InfoNoticed;

        public string Name
        {
            get
            {
                string partial = string.Empty;
                switch (Config.DataWriteMode)
                {
                    case FileDataWriteMode.Append:
                    case FileDataWriteMode.Overwrite:
                        partial = $"{Path.Combine(Config.FolderPath, Config.FileName)}";
                        break;
                    case FileDataWriteMode.New:
                        partial = $"{Config.FolderPath}中新建文件";
                        break;
                }
                return $"文件监视({partial})";
            }
        }

        public string SourceName { get; set; }

        public bool Running
        {
            get
            {
                if (Watcher == null) return false;
                return Watcher.EnableRaisingEvents;
            }
        }

        public DataDrive(Config config)
        {
            Config = config;
        }

        private string currentPath;


        private void Watcher_Changed(object sender, FileSystemEventArgs e)
        {
            if (e.FullPath == currentPath) return;
            currentPath = e.FullPath;

            if (Config.LazyRead != 0)
            {
                Thread.Sleep(Config.LazyRead * 1000);
            }

            Notify(e.FullPath);

            currentPath = string.Empty;
        }

        private void Watcher_Created(object sender, FileSystemEventArgs e)
        {
            if (e.FullPath == currentPath) return;
            currentPath = e.FullPath;

            if (Config.LazyRead != 0)
            {
                Thread.Sleep(Config.LazyRead * 1000);
            }

            Notify(e.FullPath);
            if (Config.DeleteAfterReaded)
            {
                try
                {
                    File.Delete(e.FullPath);
                }
                catch (Exception ex)
                {
                    ErrorNoticed?.Invoke(ex.Message);
                }
            }

            currentPath = string.Empty;
        }

        private void Notify(string fullPath)
        {
            SourceName = fullPath;
            if (DataRecived != null)
            {
                try
                {
                    Watcher.EnableRaisingEvents = false;
                    string content = string.Empty;
                    using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                    {
                        using (StreamReader sr = new StreamReader(fs, string.IsNullOrEmpty(Config.FileEncoding) ? Encoding.Default : Encoding.GetEncoding(Config.FileEncoding)))
                        {
                            content = sr.ReadToEnd();
                        }
                    }

                    DataRecived?.Invoke(content, fullPath);
                }
                catch (Exception ex)
                {
                    ErrorNoticed?.Invoke(ex.Message);
                }
                finally
                {
                    Watcher.EnableRaisingEvents = true;
                }
            }
        }

        public void Start()
        {
            if (Config == null) return;

            if (!Config.Valided())
            {
                WarnNoticed?.Invoke($"数据驱动配置不全,无法启用驱动.详情如下:{Config.Message}");
                return;
            }

            if (!Directory.Exists(Config.FolderPath))
            {
                WarnNoticed?.Invoke($"未能找到:{Config.FolderPath}文件夹，请到系统设置重新配置");
                return;
            }

            Watcher = new FileSystemWatcher(Config.FolderPath);
            string msg = string.Empty;
            switch (Config.DataWriteMode)
            {
                case FileDataWriteMode.Append:
                case FileDataWriteMode.Overwrite:
                    Watcher.Filter = Config.FileName;
                    Watcher.NotifyFilter = NotifyFilters.LastWrite;
                    Watcher.Changed -= Watcher_Changed;
                    Watcher.Changed += Watcher_Changed;
                    msg = $@"开始监视文件[{Config.FolderPath}\{Config.FileName}]";
                    break;
                case FileDataWriteMode.New:
                    Watcher.NotifyFilter = NotifyFilters.FileName;
                    if (!string.IsNullOrEmpty(Config.FileName))
                    {
                        Watcher.Filter = Config.FileName;
                        msg = $"开始监视文件夹[{Config.FolderPath}]中[{Config.FileName}]类型的文件";
                    }
                    else
                    {
                        msg = $"开始监视文件夹[{Config.FolderPath}]中的新建文件";
                    }
                    Watcher.Created -= Watcher_Created;
                    Watcher.Created += Watcher_Created;
                    break;
                default:
                    break;
            }
            Watcher.IncludeSubdirectories = false;
            Watcher.EnableRaisingEvents = true;
            InfoNoticed?.Invoke(msg);
            Started?.Invoke();
        }

        public void Stop()
        {
            if (Watcher == null) return;
            Watcher.EnableRaisingEvents = false;
            Stoped?.Invoke();
        }

        public void Dispose()
        {
            if (Watcher == null) return;
            Watcher.Changed -= Watcher_Changed;
            Watcher.Dispose();
        }
    }

    public class Config : IDataDriveConfig
    {
        [Display(Name = "写入模式")]
        public FileDataWriteMode DataWriteMode { get; set; }

        [Display(Name = "文件夹路径")]
        public string FolderPath { get; set; }

        [Display(Name = "要监视的文件")]
        public string FileName { get; set; }

        [Display(Name = "要监视的文件编码")]
        public string FileEncoding { get; set; }

        [Display(Name = "读取后删除源文件")]
        public bool DeleteAfterReaded { get; set; }

        [Display(Name = "延迟读取数据(秒)")]
        [Range(0, 10)]
        public int LazyRead { get; set; } = 1;

        [Display(AutoGenerateField = false)]
        public string Message { get; set; }

        public bool Valided()
        {
            switch (DataWriteMode)
            {
                case FileDataWriteMode.Append:
                case FileDataWriteMode.Overwrite:
                    if (string.IsNullOrEmpty(FolderPath))
                    {
                        Message = "[文件夹路径]不能为空";
                        return false;
                    }

                    if (string.IsNullOrEmpty(FileName))
                    {
                        Message = "[要监视的文件]不能为空";
                        return false;
                    }
                    break;
                case FileDataWriteMode.New:
                    if (string.IsNullOrEmpty(FolderPath))
                    {
                        Message = "[文件夹路径]不能为空";
                        return false;
                    }
                    break;
                default:
                    Message = "[写入模式]不能识别";
                    return false;
            }

            if (LazyRead > 10 || LazyRead < 0)
            {
                Message = "[延迟读取数据(秒)]不能大于10或小于0";
                return false;
            }

            return true;
        }
    }

    public enum FileDataWriteMode
    {
        /// <summary>
        /// 追加内容
        /// </summary>
        [Description("追加")]
        Append,
        /// <summary>
        /// 覆盖内容
        /// </summary>
        [Description("覆盖")]
        Overwrite,
        /// <summary>
        /// 新建文件
        /// </summary>
        [Description("新建")]
        New
    }
}
